﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;

using MvcEngine.Core.Repositories;
using MvcEngine.Core.Configuration;
using MvcEngine.Core;
using MvcEngine.Core.Helpers;
using MvcEngine.Core.Exceptions;

namespace MvcEngine.LinqToSqlProvider
{
    public class ContextManager : IDisposable
    {
        [ThreadStatic]
        private static RootContextManager root;

        private object locker = new object();

        private bool isCommited;

        private bool isReadOnly;

        public ContextManager(bool readOnly)
        {
            this.isReadOnly = readOnly;
            if (!InitializeAsRoot()) // if this is a nested manager
            {
                if (!this.isReadOnly && root.Owner.isReadOnly)
                    throw new MvcEngineException("The root manager is read-only. Nested managers must also be read-only.");
            }
        }

        private bool InitializeAsRoot()
        {
            if (root != null)
                return false;
            lock (locker)
            {
                if (root != null)
                    return false;
                root = new RootContextManager(this);
                if (!this.isReadOnly)
                {
                    if (root.Context.Connection.State == System.Data.ConnectionState.Closed)
                        root.Context.Connection.Open();
                    root.Context.Transaction = root.Context.Connection.BeginTransaction();
                }
                return true;
            }
        }

        public DataContext Context
        {
            get { return root.Context; }
        }

        public void Commit()
        {
            if (this.isReadOnly)
                throw new InvalidOperationException("Operation could not been completed because of context manager is readonly.");

            Context.SubmitChanges();
            this.isCommited = true;
        }

        public void Rollback()
        {
            root.IsRolledBack = true;
        }

        public void Dispose()
        {
            if (!this.isReadOnly && !this.isCommited)
                this.Rollback();

            if (root.Owner == this)
            {
                root.Dispose();
                root = null;
            }
            else
            {
                GC.SuppressFinalize(this);
            }
        }

        public void Dispose(bool disposeAll)
        {
            this.Dispose();
        }

        #region Nested Types

        private class RootContextManager : IDisposable
        {
            public ContextManager Owner;

            public DataContext Context;

            public bool IsRolledBack;

            public RootContextManager(ContextManager owner)
            {
                this.Owner = owner;
                this.Context = IoC.Resolve<IDataContextFactory>().Get();
            }

            public void Dispose()
            {
                if (!Owner.isReadOnly)
                {
                    if (this.IsRolledBack)
                        this.Context.Transaction.Rollback();
                    else
                        this.Context.Transaction.Commit();
                }
                this.Context.Dispose();
                this.Context = null;
            }
        }

        #endregion
    }
}
